use std::{collections::HashMap, str::FromStr};
use crate::http::{error::HttpParseError, utils, FetchFromBytes, FromBytes, HttpContentType};

#[derive(Debug, Clone, Default)]
pub struct HttpResponseHeaders {
    pub content_type: Option<HttpContentType>,
    pub content_length: Option<u64>,
    pub content_encoding: Option<String>,
    pub connection: Option<String>,
    pub upgrade: Option<String>,
    pub sec_websocket_accept: Option<String>,
    pub server: Option<String>,
    pub set_cookie: Vec<String>,
    pub cache_control: Option<String>,
    pub date: Option<String>,
    pub location: Option<String>,
    pub other: HashMap<String, String>,
}

impl HttpResponseHeaders {
    pub fn set_keep_alive(&mut self, keep_alive: bool) {
        self.connection = Some(if keep_alive { "keep-alive".into() } else { "close".into() });
    }

    pub fn is_keep_alive(&self) -> bool {
        !self.connection.as_ref()
            .map(|v| v.eq_ignore_ascii_case("close"))
            .unwrap_or(false)
    }
}

impl FromStr for HttpResponseHeaders {
    type Err = HttpParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut headers = Self::default();
        
        for line in s.lines() {
            if let Some((key, value)) = line.split_once(':') {
                let key = key.trim();
                let value = value.trim();

                match key.to_ascii_lowercase().as_str() {
                    "content-type" => headers.content_type = Some(HttpContentType::from_str(value)?),
                    "content-length" => headers.content_length = value.parse().ok(),
                    "content-encoding" => headers.content_encoding = Some(value.to_string()),
                    "connection" => headers.connection = Some(value.to_string()),
                    "upgrade" => headers.upgrade = Some(value.to_string()),
                    "sec-websocket-accept" => headers.sec_websocket_accept = Some(value.to_string()),
                    "server" => headers.server = Some(value.to_string()),
                    "set-cookie" => headers.set_cookie.push(value.to_string()),
                    "cache-control" => headers.cache_control = Some(value.to_string()),
                    "date" => headers.date = Some(value.to_string()),
                    "location" => headers.location = Some(value.to_string()),
                    _ => {
                        headers.other.insert(key.to_string(), value.to_string());
                    }
                }
            }
        }

        Ok(headers)
    }
}

impl FetchFromBytes for HttpResponseHeaders {
    type Err = HttpParseError;
    fn fetch_from_bytes(bytes: &[u8]) -> Result<(&[u8], Self), Self::Err> {
        match utils::find_bytes(&bytes, b"\r\n\r\n") {
            Some(line_end) => {
                let line: HttpResponseHeaders = FromBytes::from_bytes(&bytes[..line_end])?;
                Ok((&bytes[line_end+4..], line))
            }
            None => Err(HttpParseError::InvalidRequest("No '\\r\\n\\r\\n' found in response headers".into())),
        }
    }
}

impl ToString for HttpResponseHeaders {
    fn to_string(&self) -> String {
        let mut result = String::new();

        if let Some(v) = &self.content_type {
            result.push_str(&format!("Content-Type: {}\r\n", v.to_string()));
        }
        if let Some(v) = &self.content_length {
            result.push_str(&format!("Content-Length: {}\r\n", v));
        }
        if let Some(v) = &self.content_encoding {
            result.push_str(&format!("Content-Encoding: {}\r\n", v));
        }
        if let Some(v) = &self.connection {
            result.push_str(&format!("Connection: {}\r\n", v));
        }
        if let Some(v) = &self.upgrade {
            result.push_str(&format!("Upgrade: {}\r\n", v));
        }
        if let Some(v) = &self.sec_websocket_accept {
            result.push_str(&format!("Sec-websocket-Accept: {}\r\n", v));
        }
        if let Some(v) = &self.server {
            result.push_str(&format!("Server: {}\r\n", v));
        }
        for cookie in &self.set_cookie {
            result.push_str(&format!("Set-Cookie: {}\r\n", cookie));
        }
        if let Some(v) = &self.cache_control {
            result.push_str(&format!("Cache-Control: {}\r\n", v));
        }
        if let Some(v) = &self.date {
            result.push_str(&format!("Date: {}\r\n", v));
        }
        if let Some(v) = &self.location {
            result.push_str(&format!("Location: {}\r\n", v));
        }

        for (k, v) in &self.other {
            result.push_str(&format!("{}: {}\r\n", k, v));
        }

        result.push_str("\r\n");
        result
    }
}